// Copyright 2022, Uglyhack

use crate::car::Car;
use crate::{field_transform, STREET};
use geo::prelude::{Contains, Intersects};
use geo::{Polygon, Rect};
use graphics::color::WHITE;
use graphics::types::Color;
use opengl_graphics::GlGraphics;
use piston::input::RenderArgs;

/// Block of area, where you can detect if the car is in it
pub struct Block {
    top: f64,      // Y coordinate of top of space
    pub left: f64, // X coordinate of left of space
    pub width: f64,
    depth: f64,
}

impl Block {
    pub fn new_parking_space() -> Self {
        Self {
            top: 0.0,
            left: 18.0,
            width: 2.5, // Minimum = 2.4 according to NEN 2443:2000 Nl
            depth: 6.0,
        }
    }

    /// Convert to `Rectangle` (from the graphics library)
    fn to_rectangle(&self) -> graphics::types::Rectangle {
        [self.left, self.top, self.width, self.depth]
    }

    /// Generate the blocks to the left and right
    pub fn side_blocks(&self, width: f64) -> [Block; 2] {
        [
            Block {
                top: self.top,
                left: self.left - width,
                width,
                depth: self.depth,
            },
            Block {
                top: self.top,
                left: self.left + self.width,
                width,
                depth: self.depth,
            },
        ]
    }

    /// Convert to `geo::Rect`
    fn to_rect(&self) -> Rect<f64> {
        let bottom = self.top + self.depth;
        let right = self.left + self.width;

        Rect::new(
            geo::Coordinate {
                x: self.left,
                y: self.top,
            },
            geo::Coordinate {
                x: right,
                y: bottom,
            },
        )
    }

    /// Check if the car intersects this block
    pub fn intersects(&self, car: &Car) -> bool {
        self.to_rect().intersects(&car.to_polygon())
    }

    /// Check if the car is contained within this block
    pub fn contains(&self, car: &Car) -> bool {
        Polygon::from(self.to_rect()).contains(&car.to_polygon())
    }

    /// Draw `Block` as a parking space, using args at the graphics gl.
    /// Draws a gray rectangle with white outline
    ///
    /// The coordinates are rendered with 0,0 at center of screen
    pub fn draw_parking_space(&self, zoom: f64, args: &RenderArgs, gl: &mut GlGraphics) {
        use graphics::{line_from_to, rectangle};

        let bottom = self.top + self.depth;
        let right = self.left + self.width;

        let line_width = 0.1;

        gl.draw(args.viewport(), |context, gl| {
            let transform_field = field_transform(zoom, args, &context.transform);

            // Draw concrete
            rectangle(STREET, self.to_rectangle(), transform_field, gl);

            // Draw surrounding lines
            line_from_to(
                WHITE,
                line_width,
                [self.left + line_width, self.top],
                [self.left + line_width, bottom],
                transform_field,
                gl,
            );
            line_from_to(
                WHITE,
                line_width,
                [self.left, bottom - line_width],
                [right, bottom - line_width],
                transform_field,
                gl,
            );
            line_from_to(
                WHITE,
                line_width,
                [right - line_width, bottom],
                [right - line_width, self.top],
                transform_field,
                gl,
            );
        });
    }

    /// Draw as a plain colored block
    pub fn draw(&self, color: Color, zoom: f64, args: &RenderArgs, gl: &mut GlGraphics) {
        use graphics::rectangle;

        gl.draw(args.viewport(), |context, gl| {
            // Draw concrete
            rectangle(
                color,
                self.to_rectangle(),
                field_transform(zoom, args, &context.transform),
                gl,
            );
        });
    }
}
